package com.agg.domain.repository;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import com.agg.core.base.dto.EntityDTO;
import com.agg.core.base.po.BasePO;
import com.agg.core.exception.DomainException;
import com.agg.core.utils.Asserts;
import com.agg.domain.command.QueryEntityCmd;
import com.agg.domain.command.QueryListCmd;
import com.agg.domain.command.QueryPageCmd;
import com.agg.persist.xml.EntityMapperRegistry;
import com.agg.persist.xml.context.EntityContextPayload;
import com.agg.persist.xml.context.EntityMapperContext;
import com.agg.persist.xml.mapping.EntityMapper;
import com.agg.persist.xml.mapping.Join;
import com.agg.persist.xml.mapping.Ref;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.*;
import  com.agg.core.utils.Strings;
/**
 * 数据库 mybatis  存储方式
 */
@Component
public class EntityMybatisRepository extends AbstractRepository{

	public <T extends BasePO> Page<T> loadPage(QueryPageCmd<T> cmd){
		EntityMapper em=this.getEntityMapper(cmd);
		this.setMapper(cmd.getMapper());
		List<Ref> refs=em.getRefs();
		Page<T> page=this.getByPage(cmd.getPagination(),cmd.getEmid());
		setRefDataList(page.getRecords(),refs);
		return page;
	}

	public   Page<Map<String,Object>> loadMapByPage(QueryPageCmd<Map<String,Object>> cmd){
		EntityMapper em=this.getEntityMapper(cmd);
		this.setMapper(cmd.getMapper());
		List<Ref> refs=em.getRefs();
		Page<Map<String,Object>> page=this.loadMapByPage(cmd.getPagination(),cmd.getEmid());
		setRefDataList(page.getRecords(),refs);
		return page;
	}

	public   Page<Map<String,Object>> loadMapByPage(QueryPageCmd<Map<String,Object>> cmd,String sql){
		EntityMapper em=this.getEntityMapper(cmd);
		this.setMapper(cmd.getMapper());
		Page<Map<String,Object>> page=this.loadMapBySqlPage(cmd.getPagination(),sql);
		return page;
	}


	public <T extends BasePO> List<T> loadList(QueryListCmd cmd){
		List<T> list=this.getList(cmd.getEntity(),cmd.getExt(), cmd.getEmid());
		EntityMapper em=this.getEntityMapper(cmd);
		List<Ref> refs=em.getRefs();
		setRefDataList(list,refs);
		return list;
	}


	public Object loadById(QueryEntityCmd cmd){
		EntityMapper em=this.getEntityMapper(cmd);
		Object data=this.getById(cmd.getId(),em.getId());
		List<Ref> refs=em.getRefs();
		setRefData(data,refs);
		return data;
	}
	private void setRefDataList(List dataList,List<Ref> refs)  {
		if(Asserts.isNull(dataList))return;
		Object data=dataList.get(0);
		Map<String,List<Object>> keyMap= new HashMap<>();
		for(Ref ref:refs){
			//不满足规约条件，则不不用获取数据
			if(ref.getSpecification()!=null
					&&!ref.getSpecification().isSatisfiedBy(data)){
				continue;
			}
			List<Object> keys= new ArrayList<>();
			dataList.forEach(e->{
				Object val=ReflectUtil.getFieldValue(e,ref.getRefKey());
				keys.add(val);
			});
			Map<Object,Object> refDataMap=this.getEntityRefList(ref,keys);
			for(Object item:dataList){
				Object refData=refDataMap.get(ref.getRefKey());
				ReflectUtil.setFieldValue(item,ref.getName(),refData);
			}
			keyMap.put(ref.getName(),keys);
		}

	}
	/**
	 * 填充数据
	 * @param data
	 * @param refs
	 */
	private void setRefData(Object data,List<Ref> refs)  {
		if(data==null){
			return;
		}
		for (Ref ref:refs){
			//不满足规约条件，则不不用获取数据
			if(ref.getSpecification()!=null
					&&!ref.getSpecification().isSatisfiedBy(data)){
				continue;
			}
			Object refData=this.getEntityRef(ref,data);
			if(refData!=null){
				String key=ref.getName();
				ReflectUtil.setFieldValue(data,key,refData);
			}
		}

	}

	@Transactional
	public <E> Integer save(EntityDTO<E> dto){
		String  emid=dto.getEmid();
		E vo=dto.getEntity();
		//初始化上下文
		EntityMapper em= EntityMapperRegistry.getEntityMapper(emid);
		EntityContextPayload payload=new EntityContextPayload(new HashMap(),em);
		EntityMapperContext.set(payload);
		if(em==null){

			throw new DomainException("mapper not found");
		}
		if(dto.getMapper()==null){
			throw new DomainException("mybatis  mapper  is null");
		}
		this.setMapper(dto.getMapper());
		Class clz=getClass(em);
		Object data=null;
		if(vo.getClass()==clz){
			data=vo;
		}else{
			data=BeanUtil.copyProperties(vo,clz);
		}
		/**
		 * 主表主键索引
		 */

		//id 不存在为插入新数据，反之则更新数据
		Integer count=0;
		//根据id 主键索引是否为空，为空则新增
		boolean isUpdate=isUpdate(clz,data);
		if(!isUpdate){
			fillInsert(data,dto);
			count= getMapper().insert(data);
			EntityMapperContext.get().getEntityMap().put(emid,data);
		}else{
			fillUpdate(data,dto);
			count=getMapper().updateById(data);
			EntityMapperContext.get().getEntityMap().put(emid,data);
		}
		//根据join 将聚合关联数据的数据同步到数据库
		if(!Asserts.isNull(em.getJoins())){
			saveJoins(data,dto,em,isUpdate);
		}
		EntityMapperContext.clean();
		return count;
	}

	/**
	 * save all of the joins of an entity, if it has.
	 * @param entity
	 */
	private <E> void saveJoins(Object entity,EntityDTO<E> dto,
							   EntityMapper em,
							   boolean isUpdate) {
		List<Join> listOfJoins = em.getJoins();
		if(listOfJoins==null||listOfJoins.isEmpty()) return;
		E vo=dto.getEntity();
		for(Join join : listOfJoins) {
			//是否聚合：例如订单跟订单商品为聚合关系，存储或变更时，需要同步更新
			if(!join.isAggregation()) continue;
			String name = join.getName();
			EntityMapper jem= join.getEntityMapper();
			Class clz=getClass(jem);
			List<String> columns=getColumns(clz);
			String leftKey=join.getLeftKey();
			if(!ReflectUtil.hasField(entity.getClass(),leftKey)){
				String str="entity mapper id="+em.getId()+"实体找不到绑定属性：leftKey="+leftKey;
				throw new DomainException("系统错误:"+str);
			}
			Object leftKeyVal=ReflectUtil.getFieldValue(entity,leftKey);
			//数据属性
			List<String> props=getPropertys(clz);
			//获取实体对象
			Object joinVal=getJoinEntity(dto,vo,leftKeyVal,join,clz);
			if(joinVal==null)continue;
			if(List.class.isAssignableFrom(joinVal.getClass())) {
				List collection = (List)joinVal;
				//规约
				if(join.getSpecification()!=null){
					join.getSpecification().dataHandle(collection);
				}
				if(collection.isEmpty())continue;
				if(isUpdate){
					//数据为更新，则批量操做之前先删除已有数据
					deleteByColumn(jem,clz,join.getRightKey(),leftKeyVal);
				}
				//一对多：批量插入
				getMapper().insertBatch(jem.getTable(),
						columns,
						props,
						collection);//is a lot of items.
			} else {
				//规约
				if(join.getSpecification()!=null){
					join.getSpecification().dataHandle(joinVal);
				}
				if(isUpdate){
					this.getMapper().updateById(joinVal);
				}else{
					//一对一插入
					this.getMapper().insert(joinVal);//just one item.
				}
			}
			EntityMapperContext.get().getEntityMap().put(jem.getId(),joinVal);
		}
	}

	private Map<String,Object> getTableId(Class clazz,Object entity){
		List<Field> fields=  ReflectionKit.getFieldList(clazz);
		Map<String,Object> tableIdMap=new HashMap<>();
		for (Field opt:fields){
			TableId tableId = opt.getAnnotation(TableId.class);
			if(tableId==null){
				continue;
			}
			if(IdType.AUTO.equals(tableId.type())){
				Object val=ReflectUtil.getFieldValue(entity,opt.getName());
				tableIdMap.put(opt.getName(),val);
			}
		}
		return tableIdMap;
	}

	private boolean isUpdate(Class clazz,Object entity){
		List<Field> fields=  ReflectionKit.getFieldList(clazz);

		for (Field opt:fields){
			TableId tableId = opt.getAnnotation(TableId.class);
			if(tableId==null){
				continue;
			}
			if(IdType.AUTO.equals(tableId.type())){
				Object val=ReflectUtil.getFieldValue(entity,opt.getName());
				return val!=null;
			}
		}
		return false;
	}

	/**
	 * 根据列删除的字段
	 * @param rightKey
	 * @return
	 */
	private void deleteByColumn(EntityMapper em,Class clazz,String rightKey,Object val){
		String column=Strings.toLowerCase(rightKey,"_");
		Map<String,Map<String,String>> logDelMap=logicDelete(clazz);
		//逻辑删除不存在，则直接删除数据
		if(logDelMap.isEmpty()){
			getMapper().deleteBatch(em.getTable(),column,val);
		}else{
			String logicDelKey=null;
			Map.Entry<String,Map<String,String>> entry = logDelMap.entrySet().iterator().next();
			logicDelKey=Strings.toLowerCase(entry.getKey(),"_");;
			Map<String,String> logicValMap=entry.getValue();
			String delval=logicValMap.get("delval");
			//逻辑删除
			getMapper().deleteLogicBatch(em.getTable(),delval,logicDelKey,column,val );
		}

	}

	/**
	 * 获取逻辑删除字段配置
	 * @param clazz
	 * @return
	 */
	private Map<String,Map<String,String>> logicDelete(Class clazz){
		List<Field> fields=  ReflectionKit.getFieldList(clazz);
		Map<String,Map<String,String>> map=new HashMap<>();
		for (Field opt:fields){
			TableLogic logicDel = opt.getAnnotation(TableLogic.class);
			if(logicDel!=null){
				Map<String,String> logicValMap=new HashMap<>();
				if(Asserts.isNotNullOrEmpty(logicDel.delval())){
					logicValMap.put("delval",logicDel.delval());
					logicValMap.put("value",logicDel.value());
					map.put(opt.getName(),logicValMap);
				}else{
					GlobalConfig.DbConfig config=GlobalConfigUtils.defaults().getDbConfig();
					String value=config.getLogicNotDeleteValue();
					String delval=config.getLogicDeleteValue();
					logicValMap.put("delval",delval);
					logicValMap.put("value",value);
					//逻辑删除值没有设置
					map.put(opt.getName(),logicValMap);
				}
				break;
			}
		}
		return map;
	}


	/**
	 * 获取关联实体
	 * @param dto
	 * @param vo
	 * @param id
	 * @param join
	 * @param clz
	 * @param <E>
	 * @return
	 */
	private <E> Object getJoinEntity( EntityDTO<E> dto,E vo,
									 Object id,Join join,Class clz){
		Object joinVal=null;
		Object fieldVal=ReflectUtil.getFieldValue(vo, join.getName());
		if(fieldVal==null){
			return null;
		}
		String key=join.getRightKey();
		if(fieldVal.getClass()==clz){
			joinVal=fieldVal;
			//初始化关联字段
			ReflectUtil.setFieldValue(joinVal,key,id);
			fillInsert(joinVal,dto);
		}else{
			if(List.class.isAssignableFrom(fieldVal.getClass())){
				List<?> collection = (List<?>)fieldVal;
				List joinEntitys=new ArrayList();

				for(Object val:collection){
					Object joinEntity= BeanUtil.copyProperties(val,clz);
					//初始化关联字段
					ReflectUtil.setFieldValue(joinEntity,key,id);
					fillInsert(joinEntity,dto);
					joinEntitys.add(joinEntity);
				}
				joinVal=joinEntitys;
			}else{
				joinVal=BeanUtil.copyProperties(fieldVal,clz);
				//初始化关联字段
				ReflectUtil.setFieldValue(joinVal,key,id);
				//填充字段
				fillInsert(joinVal,dto);
			}
		}
		return joinVal;
	}

	private List<String> getPropertys(Class clazz){
		List<Field> fields=  ReflectionKit.getFieldList(clazz);
		List<String> props=new ArrayList<>();
		for (Field opt:fields){
			//存在tableId 则无需校验TableField 的 true或者false
			TableId tableId = opt.getAnnotation(TableId.class);
			if(tableId==null){
				//TableField  为false ：该字段非当前表映射字段
				TableField tableField = opt.getAnnotation(TableField.class);
				if(tableField!=null&&!tableField.exist()){
					continue;
				}
			}
			String value = opt.getName();
			if(Asserts.isNullOrEmpty(value))continue;
			props.add(value);
		}

		return  props;
	}

	private String getIdAuto(Class  clazz){
		List<Field> fields=  ReflectionKit.getFieldList(clazz);
		for (Field opt:fields){
			TableId tableId = opt.getAnnotation(TableId.class);
			if(tableId==null){
				continue;
			}
			if(IdType.AUTO.equals(tableId.type())){
				return opt.getName();
			}
		}
		return null;
	}

	private  List<String> getColumns(Class clazz) {
		List<Field> fields=  ReflectionKit.getFieldList(clazz);
		List<String> columns=new ArrayList<>();
		for (Field opt:fields){
			String value;
			TableId tableId = opt.getAnnotation(TableId.class);
			if (tableId == null) {
				TableField tableField = opt.getAnnotation(TableField.class);
				if(tableField!=null){
					if(!tableField.exist()) continue;
					value = tableField.value();
					if(Asserts.isNullOrEmpty(value))continue;
				}else{
					value=opt.getName();
					if(Asserts.isNullOrEmpty(value))continue;
					value=Strings.toLowerCase(value,"_");
				}
			} else {
				value = opt.getName();
				if(Asserts.isNullOrEmpty(value))continue;
				value=Strings.toLowerCase(value,"_");
			}
			columns.add(value);
		}
		return columns;
	}

	/**
	 * 插入数据填充公共字段
	 * @param data
	 * @param dto
	 */
	private  void  fillInsert(Object data, EntityDTO dto){

		if(dto.getCreateId()!=null){
			fillUserId(data,"createId",String.valueOf(dto.getCreateId()));
		}
		if(dto.getUpdateId()!=null){
			fillUserId(data,"updateId",String.valueOf(dto.getUpdateId()));
		}
		Date date=new  Date();
		fillTime(data,"createTime",date);
		fillTime(data,"updateTime",date);
		ReflectUtil.setFieldValue(data,"enabled",1);
	}

	/**
	 * 更新数据，初始化填充字段
	 * @param data
	 * @param dto
	 */
	private  void fillUpdate(Object data,EntityDTO dto){

		if(dto.getUpdateId()!=null){
			fillUserId(data,"updateId",String.valueOf(dto.getUpdateId()));
		}
		Date date=new  Date();
		fillTime(data,"updateTime",date);
	}

	private void fillUserId(Object data,String key,String userIdStr){
		if(Asserts.isNotNullOrEmpty(userIdStr)){
			Long userId=Long.parseLong(userIdStr);
			if(ReflectUtil.hasField(data.getClass(),key)){
				ReflectUtil.setFieldValue(data,key,userId);
			}
		}
	}
	private void fillTime(Object data,String key,Date date){
		if(ReflectUtil.hasField(data.getClass(),key)){
			ReflectUtil.setFieldValue(data,key,date);
		}
	}
	private Class  getClass(EntityMapper em){
		String clazzName=em.getClazz();
		Class clz=null;
		try{
			clz=Class.forName(clazzName);
		}catch (ClassNotFoundException e){
			throw new RuntimeException("系统错误");
		}
		return clz;
	}

}
